Setup and Recap
We can now have a list of genes ordered according to their evidence for being differentially-expressed. You should have saved a results_TGF_vs_CTR_annotated.csv file in the previous session.
library(tidyverse)
library(DESeq2)
results_tgf <- read_csv("out_data/DESeq_TGF_vs_CTR_annotated.csv")
Rows: 57914 Columns: 10
── Column specification ──────────────────────────────────────────
Delimiter: ","
chr (3): row, SYMBOL, GENENAME
dbl (7): baseMean, log2FoldChange, lfcSE, stat, pvalue, padj, ...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
dds <- readRDS("Robjects/dds.rds")
If not, one can be downloaded from the course website
dir.create("out_data",showWarnings = FALSE)
download.file("https://github.com/sheffield-bioinformatics-core/bms31004_2022/raw/main/out_data/DESeq_TGF_vs_CTR_annotated.csv",destfile = "out_data/DESeq_TGF_vs_CTR_annotated.csv")
results_tgf <- read_csv("out_data/DESeq_TGF_vs_CTR_annotated.csv")
download.file("https://github.com/sheffield-bioinformatics-core/bms31004_2022/raw/main/Robjects/dds.rds",destfile = "Robjects/dds.rds")
So far we have focused on individual genes that show differences between our conditions of interest. This might be of interest in some studies, but there are various approaches we could use to tell if specific pathways / process can explain the differences between conditions.
Making a heatmap from the differential expression results
A heatmap is a very popular method for visualising gene expression dataset. It shows the expression levels of a set of genes, and how those genes separate the samples in the dataset into groups. Like the PCA plot we used previously, it is an unsupervised method whereby known biological groups are added after the plot has been created.
However, we don’t use the whole expression matrix to construct the heatmap; attempting to do this will quickly cause R to crash. Moreover, the plot wouldn’t be particularly informative as most genes are not expected to change between biological conditions, or won’t even be expressed.
We often use heatmaps to show a very small set of genes. The most statistically-significant genes are a popular choice as they should discriminate well between biological groups.
The first step is to identify the Ensembl IDs of the most significant genes
# slice is a tool to extract numbered rows from a data frame
de_ids <- results_tgf %>%
arrange(padj) %>%
dplyr::slice(1:10) %>%
pull("row")
de_ids
[1] "ENSG00000172061" "ENSG00000182667" "ENSG00000120708"
[4] "ENSG00000187498" "ENSG00000139211" "ENSG00000133110"
[7] "ENSG00000119681" "ENSG00000115414" "ENSG00000060718"
[10] "ENSG00000049540"
The heatmap tool we are going to use requires a count matrix as input, and our count matrices have Ensembl IDs as row identifiers. As with the boxplots and PCA plots from before this visualisation works best for transformed counts.
library(pheatmap)
vsd <- vst(dds)
using 'avgTxLength' from assays(dds), correcting for library size
heatmap_data <- assay(vsd)[de_ids,]
pheatmap(heatmap_data)

A dendrogram is used to show the relationship between samples; whereby samples that have a short “branch” joining them being similar to each other. However, samples being next to each other on the plot does not imply that they are closely related.
The code to generate the heatmap can be modified to include useful sample annotations. The meta data for this dataset is stored in dds but must undergone a bit of modification before we can include it.
## the meta data must be a data frame
samp_anno <- data.frame(colData(dds))
## the rownames must match the columns of the data being plotted
rownames(samp_anno) <- dds$Run
pheatmap(heatmap_data,
annotation_col = samp_anno)

We can do some tidying-up as not all the columns in our annotation data frame need to be displayed
samp_anno <- dplyr::select(samp_anno, Treated, condition)
pheatmap(heatmap_data,
annotation_col = samp_anno)

Exercise:
- Make another heatmap of the dataset, but this time using the 30 genes with the largest absolute fold-change
- The cells in the heatmap are often scaled at a gene-level according to the change from mean rather than absolute value. Look at the help for
pheatmap to see how to change the heatmap
- It would be more informative to have the rows labeled according to the
SYMBOL rather than Ensembl ID. Look at the help for pheatmap; which argument do you need to change? Look back at the code used to create the de_ids variables, and modify to obtain the SYMBOL corresponding to the 30 genes with the largest absolute fold-change.
Making a heatmap from a pre-defined set of genes
The heatmaps we have generated so far, whilst being attractive, do not tell us anything new about the dataset. We specifically picked genes that we know to be differentially-expressed between groups, so shouldn’t be surprised that they separate the samples.
The authors of the original publication had a hypothesis the Extra-Cellular Matrix (ECM) genes would be altered upon TGF treatment. Now that we have associated gene names to our dataset, we can explore this hypothesis. The first step is to determine which genes belong to the pathway using the org.Hs.eg.db package
library(org.Hs.eg.db)
pathway_genes <- AnnotationDbi::select(org.Hs.eg.db,
keys = "GO:0030198",
keytype = "GO",
columns="ENSEMBL") %>% pull(ENSEMBL)
'select()' returned 1:many mapping between keys and
columns
We can follow the same steps as above to create a matrix of counts to be displayed in the heatmap. However, our first attempt to create the matrix produces an error.
heatmap_data <- assay(vsd)[pathway_genes,]
Error in assay(vsd)[pathway_genes, ] : subscript out of bounds
The error is not particularly informative but can be fixed by checking that all the names in pathway_genes actually appear in our dataset. If we try and subset our data according to row names that do not exist, then R will produce an error.
all(pathway_genes %in% rownames(dds))
[1] FALSE
table(pathway_genes %in% rownames(dds))
FALSE TRUE
29 282
We are a little bit closer, but now the pheatmap code produces an error.
pathway_genes <- pathway_genes[pathway_genes %in% rownames(dds)]
heatmap_data <- assay(vsd)[pathway_genes,]
pheatmap(heatmap_data,
annotation_col = samp_anno,scale="row")
pheatmap(heatmap_data,
annotation_col = samp_anno,scale="row")
The error does not occur when the rows are not scaled. So the error must occur during the clustering of rows (genes).
pathway_genes <- pathway_genes[pathway_genes %in% rownames(dds)]
heatmap_data <- assay(vsd)[pathway_genes,]
pheatmap(heatmap_data,
annotation_col = samp_anno)

The error implies that there is a problem with NA values, but upon inspection it doesn’t seem like there are any missing values.
any(is.na(heatmap_data))
[1] FALSE
However, we do have some rows which contain all the same values. This causes an issue, as scaling the rows involves dividing gene counts by their standard deviation. This causes NA values to appear.
rowVars(heatmap_data)
We can remove rows with 0 variance with some confidence and proceed to make the heatmap.
heatmap_data <- heatmap_data[rowVars(heatmap_data) >0, ]
pheatmap(heatmap_data,
annotation_col = samp_anno,scale="row")

On inspecting the heatmap it does appear that the genes we have selected can distinguish TGF from CTRL samples.
We can also use a volcano plot to see how differentially-expressed the genes from this pathway are in the context of the whole dataset. The technique is labeling the genes is similar to the previous workshop. Since ggplot2 can assign plot aesthetics based on our data frame, we need to introduce a column that can distinguish genes belonging to our pathway of interest.
## We choose to change the alpha parameter so that the colour of ECM stands out
results_tgf %>%
mutate(ECM_Gene = row %in% pathway_genes,"") %>%
ggplot(aes(x = log2FoldChange, y = -log10(padj), col=ECM_Gene,alpha=ECM_Gene)) + geom_point() + scale_color_manual(values = c("black","red")) + scale_alpha_manual(values=c(0.1,1))
Warning: Removed 39184 rows containing missing values (geom_point).

So it does are least appear that some of the most significant genes belong to this pathway.
Exercise: Make a plot to demonstrate whether the genes belonging to the ECM pathway are more significant (as measured by -log\(_{10}\) adjusted p-value) than genes that do not belong to the pathway.

Pathways analysis
In this section we move towards discovering if our results are biologically significant. Are the genes that we have picked statistical flukes, or are there some commonalities.
There are two different approaches one might use, and we will cover the theory behind both.
Threshold-based Gene Set Testing
For a particular pathway we need to calculate how many genes were identified as differentially-expressed and compare to how many we would be expect by chance. Or in other words, if we repeatedly generated a list of differentially-expressed genes at random how many genes from this pathway would be expect to see.
Taking our ECM genes as an example, we can then add new columns in the data frame according to whether a gene belongs to this pathway, and whether it is differentially-expressed.
go_table <- mutate(results_tgf,
inPathway = row %in% pathway_genes,
isDE = padj < 0.05 & abs(log2FoldChange) > 1)
go_table
Cross-tabulating the two new columns gives a basis for a statistical test
table(go_table$inPathway, go_table$isDE)
FALSE TRUE
FALSE 30958 533
TRUE 180 37
The Fisher’s exact test or chi-squared test (as seen here) can then be used
chisq.test(table(go_table$inPathway, go_table$isDE))
Warning in chisq.test(table(go_table$inPathway, go_table$isDE)) :
Chi-squared approximation may be incorrect
Pearson's Chi-squared test with Yates' continuity
correction
data: table(go_table$inPathway, go_table$isDE)
X-squared = 279.32, df = 1, p-value < 2.2e-16
In reality it would be impractical to test all possible pathways in this manner, so there are a number of Bioconductor packages that automate the process
Analysis with clusterProfiler
clusterProfiler is a Bioconductor package for over-representation analysis. It’s main advantage is that it provides some nice visualisation methods.
The main function is enrichGO which requires the IDs of genes found to be differentially-expressed (sigGenes) and the IDs of all genes in the dataset (universe). It uses the org.Hs.eg.db package to map between gene names and biological pathways.
library(clusterProfiler)
universe <- results_tgf %>% pull(row)
sigGenes <- results_tgf %>%
filter(padj < 0.05, !is.na(row)) %>% pull(row)
enrich_go <- enrichGO(
gene= sigGenes,
OrgDb = org.Hs.eg.db,
keyType = "ENSEMBL",
ont = "BP",
universe = universe,
qvalueCutoff = 0.05,
readable=TRUE
)
The result of enrichGo can be turned into a data frame for easier interpretation. The data frame ranks the most significant pathways and gives statistics about how many genes from that pathway are found in the list of DE genes and in the background list.
enrich_go %>% data.frame
A dot plot can show us the most enriched pathways, and the size of each.
dotplot(enrich_go,showCategory=20)

A disadvantage of the GO analysis is that it can identify terms with very similar sets of genes that perform similar biological function. We can use an upset plot to understand these overlaps.
enrichplot::upsetplot(enrich_go)

Whilst the over-representation analysis used here has revealed some interesting pathways, it may not work well in all circumstances; especially when very few genes pass the traditional cut-offs. An alternative approach is available under such circumstances.
Gene set enrichment analysis (GSEA)
An appealing feature of the GSEA method is that it does not require us to impose arbitrary cut-offs on the dataset to decide what is differentially-expressed or not. The steps in producing the input required for GSEA are i) retrieving the ranked statistics ii) naming each one according to a chosen identifier (ENSEMBL or ENTREZID for example).
The clusterProfiler package also includes an implementation of the GSEA algorithm, and the function works in much the same way as enrichGO from above.
ranked_genes <- results_tgf %>%
arrange(desc(stat)) %>%
filter(!is.na(stat))
geneList <- pull(ranked_genes, stat)
names(geneList) <- pull(ranked_genes, row)
gse_GO <- gseGO(geneList = geneList,
OrgDb = org.Hs.eg.db,
ont = "BP",keyType = "ENSEMBL")
preparing geneSet collections...
GSEA analysis...
Warning in preparePathwaysAndStats(pathways, stats, minSize, maxSize, gseaParam, :
There are ties in the preranked stats (11.31% of the list).
The order of those tied genes will be arbitrary, which may produce unexpected results.
Warning in fgseaMultilevel(...) :
For some pathways, in reality P-values are less than 1e-10. You can set the `eps` argument to zero for better estimation.
leading edge analysis...
done...
gse_GO %>% as.data.frame
An overview of the results can be provided by a “ridge plot”. This allows comparison of the test statistics for each of the top enriched pathways.
## we have changed the fig.width chunk option so that the plot displays properly
ridgeplot(gse_GO)
Picking joint bandwidth of 0.704

An upset plot can still be produced, but this time the distribution of statistics for overlapping categories can be produced.
enrichplot::upsetplot(gse_GO)

The results confirm that the ECM pathway has many differentially-expressed genes (more than we would expect by chance). Moreover, there is a tendancy for these genes to be up-regulated; as indicated by the high positive enrichment score. Another way to visualise the GSEA results, that is typically produced from the GSEA java app, is the so-called enrichment plot. The black vertical lines shown where genes from the pathway are located amongst the ranked gene list. The red dotted line shows where the highest concentration of genes is.
gseaplot(gse_GO,geneSetID = "GO:0030198")

The enrichment plot for a gene set with a high negative enrichment score reveals a different pattern. The interpretation of this results is that most genes in this pathway are down-regulated in TGF-treated samples.
gseaplot(gse_GO,geneSetID = "GO:0002283")

Exercise
- In addition to enriched GO terms,
clusterProfiler can also find enriched KEGG terms using the enrichKEGG function. There are a couple of changes that are required from enrichGO
ENTREZID have to be used as the identifer type
- the user must input an appropriate organism code. The code for humans is
hsa.
- Use the
enrichKEGG function to identify enriched KEGG terms in the analysis.
- (Optional) If you have time, use the gseKEGG to perform GSEA using KEGG terms.
?enrichKEGG
Conclusions
We have now completed the main Bioinformatics workflow for the RNA-seq analysis of our chosen dataset. The workflow has made use of a handful of Bioconductor packages, but also the tidyverse packages that we have used previously. We have spent a lot of time exploring our results, but the key steps are summarised below:-
- Importing the transcript-level counts produced by
tximport into R
- Creating a
DESeq2 dataset from these counts and our meta data
- Quality assessment to check for outliers and verify the relationships between samples
- Differential expression analysis using the
DESeq and results functions to generate gene lists
- Pathways analysis using
clusterProfiler to gain biological insights
LS0tCnRpdGxlOiAiQk1TMzUzIEJpb2luZm9ybWF0aWNzIGZvciBCaW9tZWRpY2FsIFNjaWVuY2UgLSBXZWVrIDYiCmF1dGhvcjogIk1vZHVsZSBDb29yZGluYXRvciBNYXJrIER1bm5pbmciCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazogCiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIGNzczogc3R5bGVzaGVldHMvc3R5bGVzLmNzcwplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKIyBMZWFybmluZyBvdXRjb21lcwoKLSBIb3cgY2FuIHdlIHVzZSBhICJoZWF0bWFwIiB0byB2aXN1YWxpc2Ugb3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHJlc3VsdHMKLSBIb3cgdG8gZXhwbG9yZSB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cyBmb3IgYSBnaXZlbiBwYXRod2F5Ci0gSWRlbnRpZnkgb3Zlci1yZXByZXNlbnRlZCBwYXRod2F5cyAtIGdpdmVuIGEgc2V0IG9mIGRpZmZlcmVudGlhbGx5LWV4cHJlc3NlZCBnZW5lcwotIElkZW50aWZ5IHBhdGh3YXlzIHdpdGggYSB0ZW5kYW5jeSB0byBiZSBvdmVyLSBvciB1bmRlci1leHByZXNzZWQ7IHdpdGhvdXQgaGF2aW5nIHRvIHVzZSBhbiBwLXZhbHVlIGN1dC1vZmZzIAoKIyMgU2V0dXAgYW5kIFJlY2FwCgpXZSBjYW4gbm93IGhhdmUgYSBsaXN0IG9mIGdlbmVzIG9yZGVyZWQgYWNjb3JkaW5nIHRvIHRoZWlyIGV2aWRlbmNlIGZvciBiZWluZyBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQuIFlvdSBzaG91bGQgaGF2ZSBzYXZlZCBhIGByZXN1bHRzX1RHRl92c19DVFJfYW5ub3RhdGVkLmNzdmAgZmlsZSBpbiB0aGUgcHJldmlvdXMgc2Vzc2lvbi4gCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoREVTZXEyKQpyZXN1bHRzX3RnZiAgPC0gcmVhZF9jc3YoIm91dF9kYXRhL0RFU2VxX1RHRl92c19DVFJfYW5ub3RhdGVkLmNzdiIpCmRkcyA8LSByZWFkUkRTKCJSb2JqZWN0cy9kZHMucmRzIikKYGBgCgpJZiBub3QsIG9uZSBjYW4gYmUgZG93bmxvYWRlZCBmcm9tIHRoZSBjb3Vyc2Ugd2Vic2l0ZQoKYGBge3IgZXZhbD1GQUxTRX0KZGlyLmNyZWF0ZSgib3V0X2RhdGEiLHNob3dXYXJuaW5ncyA9IEZBTFNFKQpkb3dubG9hZC5maWxlKCJodHRwczovL2dpdGh1Yi5jb20vc2hlZmZpZWxkLWJpb2luZm9ybWF0aWNzLWNvcmUvYm1zMzEwMDRfMjAyMi9yYXcvbWFpbi9vdXRfZGF0YS9ERVNlcV9UR0ZfdnNfQ1RSX2Fubm90YXRlZC5jc3YiLGRlc3RmaWxlID0gIm91dF9kYXRhL0RFU2VxX1RHRl92c19DVFJfYW5ub3RhdGVkLmNzdiIpCnJlc3VsdHNfdGdmICA8LSByZWFkX2Nzdigib3V0X2RhdGEvREVTZXFfVEdGX3ZzX0NUUl9hbm5vdGF0ZWQuY3N2IikKCmRvd25sb2FkLmZpbGUoImh0dHBzOi8vZ2l0aHViLmNvbS9zaGVmZmllbGQtYmlvaW5mb3JtYXRpY3MtY29yZS9ibXMzMTAwNF8yMDIyL3Jhdy9tYWluL1JvYmplY3RzL2Rkcy5yZHMiLGRlc3RmaWxlID0gIlJvYmplY3RzL2Rkcy5yZHMiKQoKYGBgCgpTbyBmYXIgd2UgaGF2ZSBmb2N1c2VkIG9uIGluZGl2aWR1YWwgZ2VuZXMgdGhhdCBzaG93IGRpZmZlcmVuY2VzIGJldHdlZW4gb3VyIGNvbmRpdGlvbnMgb2YgaW50ZXJlc3QuIFRoaXMgbWlnaHQgYmUgb2YgaW50ZXJlc3QgaW4gc29tZSBzdHVkaWVzLCBidXQgdGhlcmUgYXJlIHZhcmlvdXMgYXBwcm9hY2hlcyB3ZSBjb3VsZCB1c2UgdG8gdGVsbCBpZiBzcGVjaWZpYyBwYXRod2F5cyAvIHByb2Nlc3MgY2FuIGV4cGxhaW4gdGhlIGRpZmZlcmVuY2VzIGJldHdlZW4gY29uZGl0aW9ucy4KCiMjIE1ha2luZyBhIGhlYXRtYXAgZnJvbSB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gcmVzdWx0cwoKQSBoZWF0bWFwIGlzIGEgdmVyeSBwb3B1bGFyIG1ldGhvZCBmb3IgdmlzdWFsaXNpbmcgZ2VuZSBleHByZXNzaW9uIGRhdGFzZXQuIEl0IHNob3dzIHRoZSBleHByZXNzaW9uIGxldmVscyBvZiBhIHNldCBvZiBnZW5lcywgYW5kIGhvdyB0aG9zZSBnZW5lcyBzZXBhcmF0ZSB0aGUgc2FtcGxlcyBpbiB0aGUgZGF0YXNldCBpbnRvIGdyb3Vwcy4gTGlrZSB0aGUgUENBIHBsb3Qgd2UgdXNlZCBwcmV2aW91c2x5LCBpdCBpcyBhbiB1bnN1cGVydmlzZWQgbWV0aG9kIHdoZXJlYnkga25vd24gYmlvbG9naWNhbCBncm91cHMgYXJlIGFkZGVkIGFmdGVyIHRoZSBwbG90IGhhcyBiZWVuIGNyZWF0ZWQuCgpIb3dldmVyLCB3ZSBkb24ndCB1c2UgdGhlIHdob2xlIGV4cHJlc3Npb24gbWF0cml4IHRvIGNvbnN0cnVjdCB0aGUgaGVhdG1hcDsgYXR0ZW1wdGluZyB0byBkbyB0aGlzIHdpbGwgcXVpY2tseSBjYXVzZSBSIHRvIGNyYXNoLiBNb3Jlb3ZlciwgdGhlIHBsb3Qgd291bGRuJ3QgYmUgcGFydGljdWxhcmx5IGluZm9ybWF0aXZlIGFzIG1vc3QgZ2VuZXMgYXJlIG5vdCBleHBlY3RlZCB0byBjaGFuZ2UgYmV0d2VlbiBiaW9sb2dpY2FsIGNvbmRpdGlvbnMsIG9yIHdvbid0IGV2ZW4gYmUgZXhwcmVzc2VkLgoKV2Ugb2Z0ZW4gdXNlIGhlYXRtYXBzIHRvIHNob3cgYSB2ZXJ5IHNtYWxsIHNldCBvZiBnZW5lcy4gVGhlIG1vc3Qgc3RhdGlzdGljYWxseS1zaWduaWZpY2FudCBnZW5lcyBhcmUgYSBwb3B1bGFyIGNob2ljZSBhcyB0aGV5IHNob3VsZCBkaXNjcmltaW5hdGUgd2VsbCBiZXR3ZWVuIGJpb2xvZ2ljYWwgZ3JvdXBzLgoKVGhlIGZpcnN0IHN0ZXAgaXMgdG8gaWRlbnRpZnkgdGhlICpFbnNlbWJsKiBJRHMgb2YgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgZ2VuZXMKCmBgYHtyfQojIHNsaWNlIGlzIGEgdG9vbCB0byBleHRyYWN0IG51bWJlcmVkIHJvd3MgZnJvbSBhIGRhdGEgZnJhbWUKZGVfaWRzIDwtIHJlc3VsdHNfdGdmICU+JSAKICBhcnJhbmdlKHBhZGopICU+JSAKICBkcGx5cjo6c2xpY2UoMToxMCkgJT4lIAogIHB1bGwoInJvdyIpCmRlX2lkcwpgYGAKVGhlIGhlYXRtYXAgdG9vbCB3ZSBhcmUgZ29pbmcgdG8gdXNlIHJlcXVpcmVzIGEgY291bnQgbWF0cml4IGFzIGlucHV0LCBhbmQgb3VyIGNvdW50IG1hdHJpY2VzIGhhdmUgRW5zZW1ibCBJRHMgYXMgcm93IGlkZW50aWZpZXJzLiBBcyB3aXRoIHRoZSBib3hwbG90cyBhbmQgUENBIHBsb3RzIGZyb20gYmVmb3JlIHRoaXMgdmlzdWFsaXNhdGlvbiB3b3JrcyBiZXN0IGZvciB0cmFuc2Zvcm1lZCBjb3VudHMuCgpgYGB7cn0KbGlicmFyeShwaGVhdG1hcCkKdnNkIDwtIHZzdChkZHMpCmhlYXRtYXBfZGF0YSA8LSBhc3NheSh2c2QpW2RlX2lkcyxdCnBoZWF0bWFwKGhlYXRtYXBfZGF0YSkKYGBgCgpBICpkZW5kcm9ncmFtKiBpcyB1c2VkIHRvIHNob3cgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHNhbXBsZXM7IHdoZXJlYnkgc2FtcGxlcyB0aGF0IGhhdmUgYSBzaG9ydCAiYnJhbmNoIiBqb2luaW5nIHRoZW0gYmVpbmcgc2ltaWxhciB0byBlYWNoIG90aGVyLiBIb3dldmVyLCBzYW1wbGVzIGJlaW5nIG5leHQgdG8gZWFjaCBvdGhlciBvbiB0aGUgcGxvdCAqKmRvZXMgbm90KiogaW1wbHkgdGhhdCB0aGV5IGFyZSBjbG9zZWx5IHJlbGF0ZWQuCgpUaGUgY29kZSB0byBnZW5lcmF0ZSB0aGUgaGVhdG1hcCBjYW4gYmUgbW9kaWZpZWQgdG8gaW5jbHVkZSB1c2VmdWwgc2FtcGxlIGFubm90YXRpb25zLiBUaGUgbWV0YSBkYXRhIGZvciB0aGlzIGRhdGFzZXQgaXMgc3RvcmVkIGluIGBkZHNgIGJ1dCBtdXN0IHVuZGVyZ29uZSBhIGJpdCBvZiBtb2RpZmljYXRpb24gYmVmb3JlIHdlIGNhbiBpbmNsdWRlIGl0LgogCmBgYHtyfQojIyB0aGUgbWV0YSBkYXRhIG11c3QgYmUgYSBkYXRhIGZyYW1lCnNhbXBfYW5ubyA8LSBkYXRhLmZyYW1lKGNvbERhdGEoZGRzKSkKIyMgdGhlIHJvd25hbWVzIG11c3QgbWF0Y2ggdGhlIGNvbHVtbnMgb2YgdGhlIGRhdGEgYmVpbmcgcGxvdHRlZApyb3duYW1lcyhzYW1wX2Fubm8pIDwtIGRkcyRSdW4KcGhlYXRtYXAoaGVhdG1hcF9kYXRhLAogICAgICAgICBhbm5vdGF0aW9uX2NvbCA9IHNhbXBfYW5ubykKYGBgCgpXZSBjYW4gZG8gc29tZSB0aWR5aW5nLXVwIGFzIG5vdCBhbGwgdGhlIGNvbHVtbnMgaW4gb3VyIGFubm90YXRpb24gZGF0YSBmcmFtZSBuZWVkIHRvIGJlIGRpc3BsYXllZAoKYGBge3J9CnNhbXBfYW5ubyA8LSBkcGx5cjo6c2VsZWN0KHNhbXBfYW5ubywgVHJlYXRlZCwgY29uZGl0aW9uKSAKCnBoZWF0bWFwKGhlYXRtYXBfZGF0YSwKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBzYW1wX2Fubm8pCmBgYAoKPGRpdiBjbGFzcz0iZXhlcmNpc2UiPgoqKkV4ZXJjaXNlKio6IAoKLSBNYWtlIGFub3RoZXIgaGVhdG1hcCBvZiB0aGUgZGF0YXNldCwgYnV0IHRoaXMgdGltZSB1c2luZyB0aGUgMzAgZ2VuZXMgd2l0aCB0aGUgbGFyZ2VzdCBhYnNvbHV0ZSBmb2xkLWNoYW5nZQotIFRoZSBjZWxscyBpbiB0aGUgaGVhdG1hcCBhcmUgb2Z0ZW4gKnNjYWxlZCogYXQgYSBnZW5lLWxldmVsIGFjY29yZGluZyB0byB0aGUgY2hhbmdlIGZyb20gbWVhbiByYXRoZXIgdGhhbiBhYnNvbHV0ZSB2YWx1ZS4gTG9vayBhdCB0aGUgaGVscCBmb3IgYHBoZWF0bWFwYCB0byBzZWUgaG93IHRvIGNoYW5nZSB0aGUgaGVhdG1hcAotIEl0IHdvdWxkIGJlIG1vcmUgaW5mb3JtYXRpdmUgdG8gaGF2ZSB0aGUgcm93cyBsYWJlbGVkIGFjY29yZGluZyB0byB0aGUgYFNZTUJPTGAgcmF0aGVyIHRoYW4gRW5zZW1ibCBJRC4gTG9vayBhdCB0aGUgaGVscCBmb3IgYHBoZWF0bWFwYDsgd2hpY2ggYXJndW1lbnQgZG8geW91IG5lZWQgdG8gY2hhbmdlPyBMb29rIGJhY2sgYXQgdGhlIGNvZGUgdXNlZCB0byBjcmVhdGUgdGhlIGBkZV9pZHNgIHZhcmlhYmxlcywgYW5kIG1vZGlmeSB0byBvYnRhaW4gdGhlIGBTWU1CT0xgIGNvcnJlc3BvbmRpbmcgdG8gdGhlIDMwIGdlbmVzIHdpdGggdGhlIGxhcmdlc3QgYWJzb2x1dGUgZm9sZC1jaGFuZ2UuCgo8L2V4ZXJjaXNlPgoKCiMjIE1ha2luZyBhIGhlYXRtYXAgZnJvbSBhIHByZS1kZWZpbmVkIHNldCBvZiBnZW5lcwoKVGhlIGhlYXRtYXBzIHdlIGhhdmUgZ2VuZXJhdGVkIHNvIGZhciwgd2hpbHN0IGJlaW5nIGF0dHJhY3RpdmUsIGRvIG5vdCB0ZWxsIHVzIGFueXRoaW5nIG5ldyBhYm91dCB0aGUgZGF0YXNldC4gV2Ugc3BlY2lmaWNhbGx5IHBpY2tlZCBnZW5lcyB0aGF0IHdlIGtub3cgdG8gYmUgZGlmZmVyZW50aWFsbHktZXhwcmVzc2VkIGJldHdlZW4gZ3JvdXBzLCBzbyBzaG91bGRuJ3QgYmUgc3VycHJpc2VkIHRoYXQgdGhleSBzZXBhcmF0ZSB0aGUgc2FtcGxlcy4gCgpUaGUgYXV0aG9ycyBvZiB0aGUgb3JpZ2luYWwgcHVibGljYXRpb24gaGFkIGEgaHlwb3RoZXNpcyB0aGUgRXh0cmEtQ2VsbHVsYXIgTWF0cml4IChFQ00pIGdlbmVzIHdvdWxkIGJlIGFsdGVyZWQgdXBvbiBUR0YgdHJlYXRtZW50LiBOb3cgdGhhdCB3ZSBoYXZlIGFzc29jaWF0ZWQgZ2VuZSBuYW1lcyB0byBvdXIgZGF0YXNldCwgd2UgY2FuIGV4cGxvcmUgdGhpcyBoeXBvdGhlc2lzLiBUaGUgZmlyc3Qgc3RlcCBpcyB0byBkZXRlcm1pbmUgd2hpY2ggZ2VuZXMgYmVsb25nIHRvIHRoZSBwYXRod2F5IHVzaW5nIHRoZSBgb3JnLkhzLmVnLmRiYCBwYWNrYWdlCgpgYGB7cn0KbGlicmFyeShvcmcuSHMuZWcuZGIpCnBhdGh3YXlfZ2VuZXMgPC0gQW5ub3RhdGlvbkRiaTo6c2VsZWN0KG9yZy5Icy5lZy5kYiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5cyA9ICJHTzowMDMwMTk4IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAga2V5dHlwZSA9ICJHTyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbnM9IkVOU0VNQkwiKSAlPiUgcHVsbChFTlNFTUJMKQpgYGAKCldlIGNhbiBmb2xsb3cgdGhlIHNhbWUgc3RlcHMgYXMgYWJvdmUgdG8gY3JlYXRlIGEgbWF0cml4IG9mIGNvdW50cyB0byBiZSBkaXNwbGF5ZWQgaW4gdGhlIGhlYXRtYXAuIEhvd2V2ZXIsIG91ciBmaXJzdCBhdHRlbXB0IHRvIGNyZWF0ZSB0aGUgbWF0cml4IHByb2R1Y2VzIGFuIGVycm9yLgoKYGBge3IgZXZhbD1GQUxTRX0KaGVhdG1hcF9kYXRhIDwtIGFzc2F5KHZzZClbcGF0aHdheV9nZW5lcyxdCgpgYGAKYGBgCkVycm9yIGluIGFzc2F5KHZzZClbcGF0aHdheV9nZW5lcywgXSA6IHN1YnNjcmlwdCBvdXQgb2YgYm91bmRzCmBgYAoKVGhlIGVycm9yIGlzIG5vdCBwYXJ0aWN1bGFybHkgaW5mb3JtYXRpdmUgYnV0IGNhbiBiZSBmaXhlZCBieSBjaGVja2luZyB0aGF0IGFsbCB0aGUgbmFtZXMgaW4gYHBhdGh3YXlfZ2VuZXNgIGFjdHVhbGx5IGFwcGVhciBpbiBvdXIgZGF0YXNldC4gSWYgd2UgdHJ5IGFuZCBzdWJzZXQgb3VyIGRhdGEgYWNjb3JkaW5nIHRvIHJvdyBuYW1lcyB0aGF0IGRvIG5vdCBleGlzdCwgdGhlbiBSIHdpbGwgcHJvZHVjZSBhbiBlcnJvci4KCmBgYHtyfQphbGwocGF0aHdheV9nZW5lcyAlaW4lIHJvd25hbWVzKGRkcykpCnRhYmxlKHBhdGh3YXlfZ2VuZXMgJWluJSByb3duYW1lcyhkZHMpKQpgYGAKCgpXZSBhcmUgYSBsaXR0bGUgYml0IGNsb3NlciwgYnV0IG5vdyB0aGUgYHBoZWF0bWFwYCBjb2RlIHByb2R1Y2VzIGFuIGVycm9yLgoKYGBge3IgZXZhbD1GQUxTRX0KcGF0aHdheV9nZW5lcyA8LSBwYXRod2F5X2dlbmVzW3BhdGh3YXlfZ2VuZXMgJWluJSByb3duYW1lcyhkZHMpXQoKaGVhdG1hcF9kYXRhIDwtIGFzc2F5KHZzZClbcGF0aHdheV9nZW5lcyxdCgpwaGVhdG1hcChoZWF0bWFwX2RhdGEsCiAgICAgICAgIGFubm90YXRpb25fY29sID0gc2FtcF9hbm5vLHNjYWxlPSJyb3ciKQoKCmBgYAoKCmBgYApwaGVhdG1hcChoZWF0bWFwX2RhdGEsCiAgICAgICAgIGFubm90YXRpb25fY29sID0gc2FtcF9hbm5vLHNjYWxlPSJyb3ciKQpgYGAKClRoZSBlcnJvciBkb2VzIG5vdCBvY2N1ciB3aGVuIHRoZSByb3dzIGFyZSAqbm90IHNjYWxlZCouIFNvIHRoZSBlcnJvciBtdXN0IG9jY3VyIGR1cmluZyB0aGUgY2x1c3RlcmluZyBvZiByb3dzIChnZW5lcykuCgpgYGB7cn0KcGF0aHdheV9nZW5lcyA8LSBwYXRod2F5X2dlbmVzW3BhdGh3YXlfZ2VuZXMgJWluJSByb3duYW1lcyhkZHMpXQoKaGVhdG1hcF9kYXRhIDwtIGFzc2F5KHZzZClbcGF0aHdheV9nZW5lcyxdCnBoZWF0bWFwKGhlYXRtYXBfZGF0YSwKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSBzYW1wX2Fubm8pCmBgYAoKVGhlIGVycm9yIGltcGxpZXMgdGhhdCB0aGVyZSBpcyBhIHByb2JsZW0gd2l0aCBgTkFgIHZhbHVlcywgYnV0IHVwb24gaW5zcGVjdGlvbiBpdCBkb2Vzbid0IHNlZW0gbGlrZSB0aGVyZSBhcmUgYW55IG1pc3NpbmcgdmFsdWVzLgoKYGBge3J9CmFueShpcy5uYShoZWF0bWFwX2RhdGEpKQpgYGAKCkhvd2V2ZXIsIHdlIGRvIGhhdmUgc29tZSByb3dzIHdoaWNoIGNvbnRhaW4gYWxsIHRoZSBzYW1lIHZhbHVlcy4gVGhpcyBjYXVzZXMgYW4gaXNzdWUsIGFzIHNjYWxpbmcgdGhlIHJvd3MgaW52b2x2ZXMgZGl2aWRpbmcgZ2VuZSBjb3VudHMgYnkgdGhlaXIgc3RhbmRhcmQgZGV2aWF0aW9uLiBUaGlzIGNhdXNlcyBgTkFgIHZhbHVlcyB0byBhcHBlYXIuCgpgYGB7cn0Kcm93VmFycyhoZWF0bWFwX2RhdGEpCmBgYAoKV2UgY2FuIHJlbW92ZSByb3dzIHdpdGggMCB2YXJpYW5jZSB3aXRoIHNvbWUgY29uZmlkZW5jZSBhbmQgcHJvY2VlZCB0byBtYWtlIHRoZSBoZWF0bWFwLgoKYGBge3J9CmhlYXRtYXBfZGF0YSA8LSBoZWF0bWFwX2RhdGFbcm93VmFycyhoZWF0bWFwX2RhdGEpID4wLCBdCgpwaGVhdG1hcChoZWF0bWFwX2RhdGEsCiAgICAgICAgIGFubm90YXRpb25fY29sID0gc2FtcF9hbm5vLHNjYWxlPSJyb3ciKQpgYGAKCk9uIGluc3BlY3RpbmcgdGhlIGhlYXRtYXAgaXQgZG9lcyBhcHBlYXIgdGhhdCB0aGUgZ2VuZXMgd2UgaGF2ZSBzZWxlY3RlZCBjYW4gZGlzdGluZ3Vpc2ggVEdGIGZyb20gQ1RSTCBzYW1wbGVzLiAKCldlIGNhbiBhbHNvIHVzZSBhIHZvbGNhbm8gcGxvdCB0byBzZWUgaG93IGRpZmZlcmVudGlhbGx5LWV4cHJlc3NlZCB0aGUgZ2VuZXMgZnJvbSB0aGlzIHBhdGh3YXkgYXJlIGluIHRoZSBjb250ZXh0IG9mIHRoZSB3aG9sZSBkYXRhc2V0LiBUaGUgdGVjaG5pcXVlIGlzIGxhYmVsaW5nIHRoZSBnZW5lcyBpcyBzaW1pbGFyIHRvIHRoZSBwcmV2aW91cyB3b3Jrc2hvcC4gU2luY2UgYGdncGxvdDJgIGNhbiBhc3NpZ24gcGxvdCBhZXN0aGV0aWNzIGJhc2VkIG9uIG91ciBkYXRhIGZyYW1lLCB3ZSBuZWVkIHRvIGludHJvZHVjZSBhIGNvbHVtbiB0aGF0IGNhbiBkaXN0aW5ndWlzaCBnZW5lcyBiZWxvbmdpbmcgdG8gb3VyIHBhdGh3YXkgb2YgaW50ZXJlc3QuCgpgYGB7cn0KIyMgV2UgY2hvb3NlIHRvIGNoYW5nZSB0aGUgYWxwaGEgcGFyYW1ldGVyIHNvIHRoYXQgdGhlIGNvbG91ciBvZiBFQ00gc3RhbmRzIG91dApyZXN1bHRzX3RnZiAlPiUgCiAgbXV0YXRlKEVDTV9HZW5lID0gcm93ICVpbiUgcGF0aHdheV9nZW5lcywiIikgJT4lIAogIGdncGxvdChhZXMoeCA9IGxvZzJGb2xkQ2hhbmdlLCB5ID0gLWxvZzEwKHBhZGopLCBjb2w9RUNNX0dlbmUsYWxwaGE9RUNNX0dlbmUpKSArIGdlb21fcG9pbnQoKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsInJlZCIpKSArIHNjYWxlX2FscGhhX21hbnVhbCh2YWx1ZXM9YygwLjEsMSkpCmBgYAoKU28gaXQgZG9lcyBhcmUgbGVhc3QgYXBwZWFyIHRoYXQgc29tZSBvZiB0aGUgbW9zdCBzaWduaWZpY2FudCBnZW5lcyBiZWxvbmcgdG8gdGhpcyBwYXRod2F5LiAKCjxkaXYgY2xhc3M9ImV4ZXJjaXNlIj4KKipFeGVyY2lzZSoqOiBNYWtlIGEgcGxvdCB0byBkZW1vbnN0cmF0ZSB3aGV0aGVyIHRoZSBnZW5lcyBiZWxvbmdpbmcgdG8gdGhlIEVDTSBwYXRod2F5IGFyZSBtb3JlIHNpZ25pZmljYW50IChhcyBtZWFzdXJlZCBieSAtbG9nJF97MTB9JCBhZGp1c3RlZCBwLXZhbHVlKSB0aGFuIGdlbmVzIHRoYXQgZG8gbm90IGJlbG9uZyB0byB0aGUgcGF0aHdheS4KPC9kaXY+CgohW10oaW1hZ2VzL2VjbV9nZW5lc19ib3hwbG90LnBuZykKCiMgUGF0aHdheXMgYW5hbHlzaXMKCkluIHRoaXMgc2VjdGlvbiB3ZSBtb3ZlIHRvd2FyZHMgZGlzY292ZXJpbmcgaWYgb3VyIHJlc3VsdHMgYXJlICoqKmJpb2xvZ2ljYWxseSBzaWduaWZpY2FudCoqKi4gQXJlIHRoZSBnZW5lcyB0aGF0IHdlIGhhdmUgcGlja2VkIHN0YXRpc3RpY2FsIGZsdWtlcywgb3IgYXJlIHRoZXJlIHNvbWUgY29tbW9uYWxpdGllcy4gCgpUaGVyZSBhcmUgdHdvIGRpZmZlcmVudCBhcHByb2FjaGVzIG9uZSBtaWdodCB1c2UsIGFuZCB3ZSB3aWxsIGNvdmVyIHRoZSB0aGVvcnkgYmVoaW5kIGJvdGguCgojIyBUaHJlc2hvbGQtYmFzZWQgR2VuZSBTZXQgVGVzdGluZwoKRm9yIGEgcGFydGljdWxhciBwYXRod2F5IHdlIG5lZWQgdG8gY2FsY3VsYXRlIGhvdyBtYW55IGdlbmVzIHdlcmUgaWRlbnRpZmllZCBhcyBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQgYW5kIGNvbXBhcmUgdG8gKmhvdyBtYW55IHdlIHdvdWxkIGJlIGV4cGVjdCBieSBjaGFuY2UqLiBPciBpbiBvdGhlciB3b3JkcywgaWYgd2UgcmVwZWF0ZWRseSBnZW5lcmF0ZWQgYSBsaXN0IG9mIGRpZmZlcmVudGlhbGx5LWV4cHJlc3NlZCBnZW5lcyBhdCByYW5kb20gaG93IG1hbnkgZ2VuZXMgZnJvbSB0aGlzIHBhdGh3YXkgd291bGQgYmUgZXhwZWN0IHRvIHNlZS4KCgpUYWtpbmcgb3VyIEVDTSBnZW5lcyBhcyBhbiBleGFtcGxlLCB3ZSBjYW4gdGhlbiBhZGQgbmV3IGNvbHVtbnMgaW4gdGhlIGRhdGEgZnJhbWUgYWNjb3JkaW5nIHRvIHdoZXRoZXIgYSBnZW5lIGJlbG9uZ3MgdG8gdGhpcyBwYXRod2F5LCBhbmQgd2hldGhlciBpdCBpcyBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQuCgpgYGB7cn0KZ29fdGFibGUgPC0gbXV0YXRlKHJlc3VsdHNfdGdmLCAKICAgICAgICAgICAgICAgICAgIGluUGF0aHdheSA9IHJvdyAlaW4lIHBhdGh3YXlfZ2VuZXMsCiAgICAgICAgICAgICAgICAgICBpc0RFID0gcGFkaiA8IDAuMDUgJiBhYnMobG9nMkZvbGRDaGFuZ2UpID4gMSkKZ29fdGFibGUKYGBgCgpDcm9zcy10YWJ1bGF0aW5nIHRoZSB0d28gbmV3IGNvbHVtbnMgZ2l2ZXMgYSBiYXNpcyBmb3IgYSBzdGF0aXN0aWNhbCB0ZXN0CgpgYGB7cn0KdGFibGUoZ29fdGFibGUkaW5QYXRod2F5LCBnb190YWJsZSRpc0RFKQpgYGAKClRoZSBGaXNoZXIncyBleGFjdCB0ZXN0IG9yIGNoaS1zcXVhcmVkIHRlc3QgKGFzIHNlZW4gaGVyZSkgY2FuIHRoZW4gYmUgdXNlZAoKYGBge3J9CmNoaXNxLnRlc3QodGFibGUoZ29fdGFibGUkaW5QYXRod2F5LCBnb190YWJsZSRpc0RFKSkKYGBgCiAgICAKSW4gcmVhbGl0eSBpdCB3b3VsZCBiZSBpbXByYWN0aWNhbCB0byB0ZXN0IGFsbCBwb3NzaWJsZSBwYXRod2F5cyBpbiB0aGlzIG1hbm5lciwgc28gdGhlcmUgYXJlIGEgbnVtYmVyIG9mIEJpb2NvbmR1Y3RvciBwYWNrYWdlcyB0aGF0IGF1dG9tYXRlIHRoZSBwcm9jZXNzCgoKIyMjIEFuYWx5c2lzIHdpdGggY2x1c3RlclByb2ZpbGVyCgpgY2x1c3RlclByb2ZpbGVyYCBpcyBhIEJpb2NvbmR1Y3RvciBwYWNrYWdlIGZvciBvdmVyLXJlcHJlc2VudGF0aW9uIGFuYWx5c2lzLiBJdCdzIG1haW4gYWR2YW50YWdlIGlzIHRoYXQgaXQgcHJvdmlkZXMgc29tZSBuaWNlIHZpc3VhbGlzYXRpb24gbWV0aG9kcy4KClRoZSBtYWluIGZ1bmN0aW9uIGlzIGBlbnJpY2hHT2Agd2hpY2ggcmVxdWlyZXMgdGhlIElEcyBvZiBnZW5lcyBmb3VuZCB0byBiZSBkaWZmZXJlbnRpYWxseS1leHByZXNzZWQgKGBzaWdHZW5lc2ApIGFuZCB0aGUgSURzIG9mICphbGwqIGdlbmVzIGluIHRoZSBkYXRhc2V0IChgdW5pdmVyc2VgKS4gSXQgdXNlcyB0aGUgYG9yZy5Icy5lZy5kYmAgcGFja2FnZSB0byBtYXAgYmV0d2VlbiBnZW5lIG5hbWVzIGFuZCBiaW9sb2dpY2FsIHBhdGh3YXlzLgoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpCnVuaXZlcnNlIDwtIHJlc3VsdHNfdGdmICU+JSBwdWxsKHJvdykKc2lnR2VuZXMgPC0gcmVzdWx0c190Z2YgJT4lIAogIGZpbHRlcihwYWRqIDwgMC4wNSwgIWlzLm5hKHJvdykpICU+JSBwdWxsKHJvdykKCmVucmljaF9nbyA8LSBlbnJpY2hHTygKICBnZW5lPSBzaWdHZW5lcywKICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwKICBrZXlUeXBlID0gIkVOU0VNQkwiLAogIG9udCA9ICJCUCIsCiAgdW5pdmVyc2UgPSB1bml2ZXJzZSwKICBxdmFsdWVDdXRvZmYgPSAwLjA1LAogIHJlYWRhYmxlPVRSVUUKKQoKYGBgCgpUaGUgcmVzdWx0IG9mIGBlbnJpY2hHb2AgY2FuIGJlIHR1cm5lZCBpbnRvIGEgZGF0YSBmcmFtZSBmb3IgZWFzaWVyIGludGVycHJldGF0aW9uLiBUaGUgZGF0YSBmcmFtZSByYW5rcyB0aGUgbW9zdCBzaWduaWZpY2FudCBwYXRod2F5cyBhbmQgZ2l2ZXMgc3RhdGlzdGljcyBhYm91dCBob3cgbWFueSBnZW5lcyBmcm9tIHRoYXQgcGF0aHdheSBhcmUgZm91bmQgaW4gdGhlIGxpc3Qgb2YgREUgZ2VuZXMgYW5kIGluIHRoZSBiYWNrZ3JvdW5kIGxpc3QuCgpgYGB7cn0KZW5yaWNoX2dvICU+JSBkYXRhLmZyYW1lCmBgYAoKQSBkb3QgcGxvdCBjYW4gc2hvdyB1cyB0aGUgbW9zdCBlbnJpY2hlZCBwYXRod2F5cywgYW5kIHRoZSBzaXplIG9mIGVhY2guCgpgYGB7cn0KZG90cGxvdChlbnJpY2hfZ28sc2hvd0NhdGVnb3J5PTIwKQpgYGAKQSBkaXNhZHZhbnRhZ2Ugb2YgdGhlIEdPIGFuYWx5c2lzIGlzIHRoYXQgaXQgY2FuIGlkZW50aWZ5IHRlcm1zIHdpdGggdmVyeSBzaW1pbGFyIHNldHMgb2YgZ2VuZXMgdGhhdCBwZXJmb3JtIHNpbWlsYXIgYmlvbG9naWNhbCBmdW5jdGlvbi4gV2UgY2FuIHVzZSBhbiBbdXBzZXQgcGxvdF0oaHR0cHM6Ly91cHNldC5hcHAvKSB0byB1bmRlcnN0YW5kIHRoZXNlIG92ZXJsYXBzLgoKYGBge3J9CmVucmljaHBsb3Q6OnVwc2V0cGxvdChlbnJpY2hfZ28pCmBgYAoKV2hpbHN0IHRoZSBvdmVyLXJlcHJlc2VudGF0aW9uIGFuYWx5c2lzIHVzZWQgaGVyZSBoYXMgcmV2ZWFsZWQgc29tZSBpbnRlcmVzdGluZyBwYXRod2F5cywgaXQgbWF5IG5vdCB3b3JrIHdlbGwgaW4gYWxsIGNpcmN1bXN0YW5jZXM7IGVzcGVjaWFsbHkgd2hlbiB2ZXJ5IGZldyBnZW5lcyBwYXNzIHRoZSB0cmFkaXRpb25hbCBjdXQtb2Zmcy4gQW4gYWx0ZXJuYXRpdmUgYXBwcm9hY2ggaXMgYXZhaWxhYmxlIHVuZGVyIHN1Y2ggY2lyY3Vtc3RhbmNlcy4KCiMjIEdlbmUgc2V0IGVucmljaG1lbnQgYW5hbHlzaXMgKEdTRUEpCgpBbiBhcHBlYWxpbmcgZmVhdHVyZSBvZiB0aGUgKipHU0VBKiogbWV0aG9kIGlzIHRoYXQgaXQgZG9lcyBub3QgcmVxdWlyZSB1cyB0byBpbXBvc2UgYXJiaXRyYXJ5IGN1dC1vZmZzIG9uIHRoZSBkYXRhc2V0IHRvIGRlY2lkZSB3aGF0IGlzIGRpZmZlcmVudGlhbGx5LWV4cHJlc3NlZCBvciBub3QuIFRoZSBzdGVwcyBpbiBwcm9kdWNpbmcgdGhlIGlucHV0IHJlcXVpcmVkIGZvciBHU0VBIGFyZSBpKSByZXRyaWV2aW5nIHRoZSByYW5rZWQgc3RhdGlzdGljcyBpaSkgbmFtaW5nIGVhY2ggb25lIGFjY29yZGluZyB0byBhIGNob3NlbiBpZGVudGlmaWVyIChgRU5TRU1CTGAgb3IgYEVOVFJFWklEYCBmb3IgZXhhbXBsZSkuCgpUaGUgYGNsdXN0ZXJQcm9maWxlcmAgcGFja2FnZSBhbHNvIGluY2x1ZGVzIGFuIGltcGxlbWVudGF0aW9uIG9mIHRoZSBHU0VBIGFsZ29yaXRobSwgYW5kIHRoZSBmdW5jdGlvbiB3b3JrcyBpbiBtdWNoIHRoZSBzYW1lIHdheSBhcyBgZW5yaWNoR09gIGZyb20gYWJvdmUuCgpgYGB7cn0KcmFua2VkX2dlbmVzIDwtIHJlc3VsdHNfdGdmICU+JSAKICBhcnJhbmdlKGRlc2Moc3RhdCkpICU+JSAKICBmaWx0ZXIoIWlzLm5hKHN0YXQpKQpnZW5lTGlzdCA8LSBwdWxsKHJhbmtlZF9nZW5lcywgc3RhdCkKbmFtZXMoZ2VuZUxpc3QpIDwtIHB1bGwocmFua2VkX2dlbmVzLCByb3cpCmdzZV9HTyAgPC0gZ3NlR08oZ2VuZUxpc3QgPSBnZW5lTGlzdCwKICAgICAgICBPcmdEYiA9IG9yZy5Icy5lZy5kYiwKICAgICAgICBvbnQgPSAiQlAiLGtleVR5cGUgPSAiRU5TRU1CTCIpCmBgYAoKYGBge3J9CmdzZV9HTyAlPiUgYXMuZGF0YS5mcmFtZQpgYGAKQW4gb3ZlcnZpZXcgb2YgdGhlIHJlc3VsdHMgY2FuIGJlIHByb3ZpZGVkIGJ5IGEgInJpZGdlIHBsb3QiLiBUaGlzIGFsbG93cyBjb21wYXJpc29uIG9mIHRoZSB0ZXN0IHN0YXRpc3RpY3MgZm9yIGVhY2ggb2YgdGhlIHRvcCBlbnJpY2hlZCBwYXRod2F5cy4gCgpgYGB7ciBmaWcud2lkdGg9OH0KIyMgd2UgaGF2ZSBjaGFuZ2VkIHRoZSBmaWcud2lkdGggY2h1bmsgb3B0aW9uIHNvIHRoYXQgdGhlIHBsb3QgZGlzcGxheXMgcHJvcGVybHkKcmlkZ2VwbG90KGdzZV9HTykKYGBgCkFuIHVwc2V0IHBsb3QgY2FuIHN0aWxsIGJlIHByb2R1Y2VkLCBidXQgdGhpcyB0aW1lIHRoZSBkaXN0cmlidXRpb24gb2Ygc3RhdGlzdGljcyBmb3Igb3ZlcmxhcHBpbmcgY2F0ZWdvcmllcyBjYW4gYmUgcHJvZHVjZWQuCgpgYGB7cn0KZW5yaWNocGxvdDo6dXBzZXRwbG90KGdzZV9HTykKYGBgCgpUaGUgcmVzdWx0cyBjb25maXJtIHRoYXQgdGhlIEVDTSBwYXRod2F5IGhhcyBtYW55IGRpZmZlcmVudGlhbGx5LWV4cHJlc3NlZCBnZW5lcyAobW9yZSB0aGFuIHdlIHdvdWxkIGV4cGVjdCBieSBjaGFuY2UpLiBNb3Jlb3ZlciwgdGhlcmUgaXMgYSB0ZW5kYW5jeSBmb3IgdGhlc2UgZ2VuZXMgdG8gYmUgdXAtcmVndWxhdGVkOyBhcyBpbmRpY2F0ZWQgYnkgdGhlIGhpZ2ggcG9zaXRpdmUgZW5yaWNobWVudCBzY29yZS4gQW5vdGhlciB3YXkgdG8gdmlzdWFsaXNlIHRoZSBHU0VBIHJlc3VsdHMsIHRoYXQgaXMgdHlwaWNhbGx5IHByb2R1Y2VkIGZyb20gdGhlIEdTRUEgamF2YSBhcHAsIGlzIHRoZSBzby1jYWxsZWQgZW5yaWNobWVudCBwbG90LiBUaGUgYmxhY2sgdmVydGljYWwgbGluZXMgc2hvd24gd2hlcmUgZ2VuZXMgZnJvbSB0aGUgcGF0aHdheSBhcmUgbG9jYXRlZCBhbW9uZ3N0IHRoZSByYW5rZWQgZ2VuZSBsaXN0LiBUaGUgcmVkIGRvdHRlZCBsaW5lIHNob3dzIHdoZXJlIHRoZSBoaWdoZXN0IGNvbmNlbnRyYXRpb24gb2YgZ2VuZXMgaXMuCgpgYGB7cn0KZ3NlYXBsb3QoZ3NlX0dPLGdlbmVTZXRJRCA9ICJHTzowMDMwMTk4IikKYGBgClRoZSBlbnJpY2htZW50IHBsb3QgZm9yIGEgZ2VuZSBzZXQgd2l0aCBhIGhpZ2ggbmVnYXRpdmUgZW5yaWNobWVudCBzY29yZSByZXZlYWxzIGEgZGlmZmVyZW50IHBhdHRlcm4uIFRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGlzIHJlc3VsdHMgaXMgdGhhdCBtb3N0IGdlbmVzIGluIHRoaXMgcGF0aHdheSBhcmUgZG93bi1yZWd1bGF0ZWQgaW4gVEdGLXRyZWF0ZWQgc2FtcGxlcy4KCmBgYHtyfQpnc2VhcGxvdChnc2VfR08sZ2VuZVNldElEID0gIkdPOjAwMDIyODMiKQpgYGAKCiMjIEV4ZXJjaXNlCgo8ZGl2IGNsYXNzPSJleGVyY2lzZSI+CgotIEluIGFkZGl0aW9uIHRvIGVucmljaGVkIEdPIHRlcm1zLCBgY2x1c3RlclByb2ZpbGVyYCBjYW4gYWxzbyBmaW5kIGVucmljaGVkIFtLRUdHXShodHRwczovL3d3dy5nZW5vbWUuanAva2VnZy8pIHRlcm1zIHVzaW5nIHRoZSBgZW5yaWNoS0VHR2AgZnVuY3Rpb24uIFRoZXJlIGFyZSBhIGNvdXBsZSBvZiBjaGFuZ2VzIHRoYXQgYXJlIHJlcXVpcmVkIGZyb20gYGVucmljaEdPYAogIC0gYEVOVFJFWklEYCBoYXZlIHRvIGJlIHVzZWQgYXMgdGhlIGlkZW50aWZlciB0eXBlCiAgLSB0aGUgdXNlciBtdXN0IGlucHV0IGFuIGFwcHJvcHJpYXRlIFtvcmdhbmlzbSBjb2RlXShodHRwczovL3d3dy5nZW5vbWUuanAva2VnZy9jYXRhbG9nL29yZ19saXN0Lmh0bWwpLiBUaGUgY29kZSBmb3IgaHVtYW5zIGlzIGBoc2FgLgotIFVzZSB0aGUgYGVucmljaEtFR0dgIGZ1bmN0aW9uIHRvIGlkZW50aWZ5IGVucmljaGVkIEtFR0cgdGVybXMgaW4gdGhlIGFuYWx5c2lzLgotIChPcHRpb25hbCkgSWYgeW91IGhhdmUgdGltZSwgdXNlIHRoZSBnc2VLRUdHIHRvIHBlcmZvcm0gR1NFQSB1c2luZyBLRUdHIHRlcm1zLgoKPC9kaXY+CgpgYGB7cn0KP2VucmljaEtFR0cKYGBgCgojIyBDb25jbHVzaW9ucwoKV2UgaGF2ZSBub3cgY29tcGxldGVkIHRoZSBtYWluIEJpb2luZm9ybWF0aWNzIHdvcmtmbG93IGZvciB0aGUgUk5BLXNlcSBhbmFseXNpcyBvZiBvdXIgY2hvc2VuIGRhdGFzZXQuIFRoZSB3b3JrZmxvdyBoYXMgbWFkZSB1c2Ugb2YgYSBoYW5kZnVsIG9mIEJpb2NvbmR1Y3RvciBwYWNrYWdlcywgYnV0IGFsc28gdGhlIGB0aWR5dmVyc2VgIHBhY2thZ2VzIHRoYXQgd2UgaGF2ZSB1c2VkIHByZXZpb3VzbHkuIFdlIGhhdmUgc3BlbnQgYSBsb3Qgb2YgdGltZSBleHBsb3Jpbmcgb3VyIHJlc3VsdHMsIGJ1dCB0aGUga2V5IHN0ZXBzIGFyZSBzdW1tYXJpc2VkIGJlbG93Oi0KCi0gSW1wb3J0aW5nIHRoZSB0cmFuc2NyaXB0LWxldmVsIGNvdW50cyBwcm9kdWNlZCBieSBgdHhpbXBvcnRgIGludG8gUgotIENyZWF0aW5nIGEgYERFU2VxMmAgZGF0YXNldCBmcm9tIHRoZXNlIGNvdW50cyBhbmQgb3VyICptZXRhIGRhdGEqCi0gUXVhbGl0eSBhc3Nlc3NtZW50IHRvIGNoZWNrIGZvciBvdXRsaWVycyBhbmQgdmVyaWZ5IHRoZSByZWxhdGlvbnNoaXBzIGJldHdlZW4gc2FtcGxlcwotIERpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHVzaW5nIHRoZSBgREVTZXFgIGFuZCBgcmVzdWx0c2AgZnVuY3Rpb25zIHRvIGdlbmVyYXRlIGdlbmUgbGlzdHMKLSBQYXRod2F5cyBhbmFseXNpcyB1c2luZyBgY2x1c3RlclByb2ZpbGVyYCB0byBnYWluIGJpb2xvZ2ljYWwgaW5zaWdodHMKCg==